/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <cstddef>
#include <cstdint>
#include <malloc.h>
#include <map>
#include <securec.h>
#include <vector>

#include <fuzzer/FuzzedDataProvider.h>

#include "se_base_services_defines.h"
#include "tee_internal_se_api_mock.h"

#define ERR (-1)
#define SUCCESS 0

#define MODULE_ID_EQUIPMENT (0x0)
#define MODULE_ID_WEAVER (0x50)

#define SEC_STORAGE_WITH_CMD(cmd) SERVICE_WITH_CMD_ID(SERVICE_ID_SEC_STORAGE, cmd)

std::vector<std::pair<uint32_t, std::vector<uint32_t>>> serviceCmd = {
    {
        MODULE_ID_WEAVER,
        {
            SERVICE_WITH_CMD_ID(MODULE_ID_WEAVER, 1),
            SERVICE_WITH_CMD_ID(MODULE_ID_WEAVER, 2),
            SERVICE_WITH_CMD_ID(MODULE_ID_WEAVER, 3),
            SERVICE_WITH_CMD_ID(MODULE_ID_WEAVER, 4),
            SERVICE_WITH_CMD_ID(MODULE_ID_WEAVER, CMD_IS_SERVICE_AVALIABLE),
            SERVICE_WITH_CMD_ID(MODULE_ID_WEAVER, CMD_SET_SERVICE_CONFIG),
        },
    },
    {
        SERVICE_ID_SEC_STORAGE,
        {
            SEC_STORAGE_WITH_CMD(1),
            SEC_STORAGE_WITH_CMD(2),
            SEC_STORAGE_WITH_CMD(3),
            SEC_STORAGE_WITH_CMD(4),
            SEC_STORAGE_WITH_CMD(5),
            SEC_STORAGE_WITH_CMD(6),
            SEC_STORAGE_WITH_CMD(7),
            SEC_STORAGE_WITH_CMD(8),
            SEC_STORAGE_WITH_CMD(9),
            SEC_STORAGE_WITH_CMD(10),
            SEC_STORAGE_WITH_CMD(11),
            SEC_STORAGE_WITH_CMD(12),
            SEC_STORAGE_WITH_CMD(13),
            SEC_STORAGE_WITH_CMD(14),
            SEC_STORAGE_WITH_CMD(CMD_IS_SERVICE_AVALIABLE),
            SEC_STORAGE_WITH_CMD(CMD_SET_SERVICE_CONFIG),
        },
    },
    {
        MODULE_ID_EQUIPMENT,
        {201, 202},
    },
};

extern "C" ResultCode ServiceInvokeCommand(uint32_t sender, uint32_t sid, uint32_t cmd, SharedDataBuffer *sharedData);

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
    if (data == nullptr || size < sizeof(uint32_t)) {
        return ERR;
    }

    FuzzedDataProvider fdp(data, size);

    uint8_t sIndex = fdp.ConsumeIntegral<uint8_t>() % serviceCmd.size();
    uint8_t cIndex = fdp.ConsumeIntegral<uint8_t>() % serviceCmd[sIndex].second.size();

    uint32_t serviceId = serviceCmd[sIndex].first;
    uint32_t command = serviceCmd[sIndex].second[cIndex];

    auto buffer = fdp.ConsumeBytes<uint8_t>(fdp.ConsumeIntegralInRange<uint16_t>(0, 512)); // max input is 512
    SharedDataBuffer buff = {
        .data = buffer.data(),
        .dataSize = (uint32_t)buffer.size(),
        .dataMaxSize = (uint32_t)buffer.size(),
    };

    // mock the card
    using OHOS::SeBaseServices::UnitTest::TeeInternalSeApiMock;
    TeeInternalSeApiMock::SeConfig config = {
        {
            "eSE_spi_0",
            {
                // weaver app
                {0xf0, 0xbb, 0xaa, 0xce, 0xaa, 0x68, 0x77, 0x5f, 0x77, 0x65, 0x61, 0x76, 0x65, 0x72, 0x00, 0x00},
                // sec storage app
                {0xf0, 0x00, 0x00, 0x48, 0x4d, 0x53, 0x45, 0x43, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x00},
                // ssd
                {0xf0, 0x00, 0x00, 0x48, 0x4d, 0x53, 0x45, 0x43},
            },
        },
    };

    auto process = [&fdp](TEE_SEChannelHandle, void *, uint32_t, void *response, uint32_t *responseLen) {
        if (response == nullptr || responseLen == nullptr) {
            return 0;
        }

        auto rsp = fdp.ConsumeRemainingBytes<uint8_t>();
        if (rsp.size() < 2) {
            return 0;
        }
        if (memcpy_s(response, *responseLen, rsp.data(), rsp.size()) == EOK) {
            static_cast<uint8_t *>(response)[rsp.size() - 1] = 0x00; // make the sw 0x90 0x00
            static_cast<uint8_t *>(response)[rsp.size() - 2] = 0x90; // make the sw 0x90 0x00
            *responseLen = static_cast<uint32_t>(rsp.size());
        }
        return 0;
    };

    ::testing::NiceMock<TeeInternalSeApiMock> mock(config);
    ON_CALL(mock, TEE_SEChannelTransmit).WillByDefault(process);

    (void)ServiceInvokeCommand(0, serviceId, command, &buff);

    return 0;
}
